#include "StdAfx.h"
#include "AMMimeUtils.h"
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
////////// a small helper function, to do case insensitive search ////////

#define strupos(x, y) (strustr(x, y) != NULL ? strustr(x, y) - x : -1) //char version
#pragma warning(disable:4996)
char* strustr(char *source, char *s)
{
  //make an uppercase copy af source and s
  char *csource = _strdup(source);
  char *cs = _strdup(s);
  _strupr_s(csource,100);
  _strupr_s(cs,100);
  //find cs in csource...
  char *result = strstr(csource, cs);
  if (result != NULL)
  {
    //cs is somewhere in csource
    int pos = result - csource;
    result = source;
    result += pos;
  }
  //clean up
//  free(csource);
//  free(cs);
  return result;
}

////////// The "real" decoding stuff //////////

const char base64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

#define SKIP '\202'
#define NOSKIP 'A'
#define MaxLineLength 76

const char base64map[] =
{
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,  SKIP,  SKIP,   SKIP,  SKIP,  SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,    62,   SKIP,   SKIP,   SKIP,    63,
      52,    53,    54,    55,    56,    57,    58,    59,
      60,    61,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,    0 ,    1 ,    2 ,    3 ,    4 ,    5 ,    6 ,
      7 ,    8 ,    9 ,    10,    11,    12,    13,    14,
      15,    16,    17,    18,    19,    20,    21,    22,
      23,    24,    25,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,    26,    27,    28,    29,    30,    31,    32,
      33,    34,    35,    36,    37,    38,    39,    40,
      41,    42,    43,    44,    45,    46,    47,    48,
      49,    50,    51,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP
};

const char hexmap[] = {
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
      0 ,    1 ,    2 ,    3 ,    4 ,    5 ,    6 ,    7 ,
      8 ,    9 ,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,    10,    11,    12,    13,    14,    15,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP
};

const char QpEncodeMap[] = {
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   NOSKIP,   SKIP,   SKIP,   NOSKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     NOSKIP,   SKIP,   SKIP,   SKIP,   SKIP,   NOSKIP,   NOSKIP,   NOSKIP,
     NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,
     NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,
     NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   SKIP,   NOSKIP,   NOSKIP,
     SKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,
     NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,
     NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,
     NOSKIP,   NOSKIP,   NOSKIP,   SKIP,   SKIP,   SKIP,   SKIP,   NOSKIP,
     SKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,
     NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,
     NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,   NOSKIP,
     NOSKIP,   NOSKIP,   NOSKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
     SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP
};

char* MimeDecodeMailHeaderField(char *s)
{
	if (s == NULL) return s;
	//if s ends with a crlf pair, remove it...
	if (s[strlen(s) - 2] == '\r')
	{
		s[strlen(s) - 2] = '\0';
	}
	//remove starting spaces...
	char *s1 = s;
	char *rest = NULL;
	char *start = NULL;
	while (*s1 == ' ') s1++;

	if (strupos(s1, "=?") > 0)
	{
		int startend = strupos(s1, "=?");
		start = (char*)_alloca((startend + 1) * sizeof(char));
		memcpy(start, s, startend);
		start[startend] = '\0';
		s1 += startend;
	}

	if (strupos(s1, "=?") == 0)
	{
		//we have to decode
		//if there are text after "?=" we have to move it to another string, so the decoder don't delete it...
		//if isn't qp-encoded and the first char is encoded, we have to move past the "?=", to find the next...

		int plainpos = strupos(s1, "Q?=");
		if (plainpos > 0)
		{
			plainpos += 3;
			char *m = s1 + plainpos;
			plainpos += strupos(m, "?=");
		}
		else
		{
			plainpos = strupos(s1, "?=");
		}

		if (plainpos > 1)
		{
			char *mid = s1 + plainpos + 2;
			s1[plainpos] = '\0';
			if (strlen(mid) > 0)
			{
				//we have some text we need to "cut"
				rest = (char*)_alloca(strlen(mid) + sizeof(char));
				memcpy(rest, mid, strlen(mid));
			}
		}
		//do the decode...
		char *subj = s1;
		char *decodedText=NULL;
		if (strupos(s1, "?Q?") > 0)
		{
			//it's quoted printable
			//find the right "starting point" in the string...
			int pos = strupos(s1, "?Q?");
			s1 += pos;
			if (strlen(s1) < 4) return s; //this should never happend, if the sender is RFC compliant...
			s1 += 3;
			CQPUtils qp;
			decodedText = qp.Decode(s1);
		}
		if (strupos(s1, "?B?") > 0)
		{
			//it's base64
			//find the right "starting point" in the string...
			int pos = strupos(s1, "?B?");
			s1 += pos;
			if (strlen(s1) < 4) return s; //this should never happend, if the sender is RFC compliant...
			s1 += 3;
			CBase64Utils bu;
			int sLen = strlen(s1);
			decodedText = bu.Decode(s1, &sLen);
		}
		//put the decoded text back in s, and append rest...

		int alloclen = strlen(decodedText) + 1;
		if (start != NULL) alloclen += strlen(start);
		if (rest != NULL) alloclen += strlen(rest);
		alloclen *= sizeof(char);
		s = (char*)realloc(s, alloclen);
		s[0] = '\0';
		if (start != NULL)
		{
			lstrcatA(s, start);
		}
		lstrcatA(s, decodedText);
		if (rest != NULL)
		{
			lstrcatA(s, rest);
		}
		free(decodedText);
	}
	return s;
}

CBase64Utils::CBase64Utils()
{
	ErrorCode = 0;
}

CBase64Utils::~CBase64Utils()
{
}

char* CBase64Utils::Encode(char *input, int bufsize)
{
	int alsize = ((bufsize * 4) / 3);
	char *finalresult = (char*)calloc(alsize + ((alsize / 76) * 2) + (10 * sizeof(char)), sizeof(char));
	int count = 0;
	int LineLen = 0;
	char* fresult = finalresult;
	char *s = input;
	int tmp = 0;
	//let's step through the buffer and encode it...
	while (count <= bufsize)
	{
		if (count % 3 == 0 && count != 0)
		{
			tmp >>= 8;
			tmp &= 0xFFFFFF;
			//we have 4 new b64 chars, add them to finalresult
			int mid = tmp;
			mid >>= 18;
			mid &= 0x3F;
			*(fresult++) = base64chars[mid];
			LineLen++;
			mid = tmp;
			mid >>= 12;
			mid &= 0x3F;
			*(fresult++) = base64chars[mid];
			LineLen++;
			mid = tmp;
			mid >>= 6;
			mid &= 0x3F;
			*(fresult++) = base64chars[mid];
			LineLen++;
			mid = tmp;
			mid &= 0x3F;
			*(fresult++) = base64chars[mid];
			LineLen++;
			//reset tmp
			tmp = 0;
			//should we break the line...
			if (LineLen >= MaxLineLength)
			{
				*(fresult++) = '\r';
				*(fresult++) = '\n';
				LineLen = 0;
			}
			if (bufsize - count < 3)
				break;
		}
		unsigned char mid = (256 - (0 - *s));
		tmp |= mid;
		tmp <<= 8;
		count++;
		s++;
	}
	//do we have some chars left...
	int rest = (bufsize - count) % 3;
	if (rest != 0)
	{
		tmp = 0;
		int i;
		for (i = 0; i < 3; i++)
		{
			if (i < rest)
			{
				unsigned char mid = (256 - (0 - *s));
				tmp |= mid;
				tmp |= *s;
				tmp <<= 8;
				count++;
				s++;
			}
			else
			{
				tmp |= 0;
				tmp <<= 8;
			}
		}
		tmp >>= 8;
		tmp &= 0xFFFFFF;
		//we have some new b64 chars, add them to finalresult
		int mid = tmp;
		if (rest >= 1)
		{
			mid >>= 18;
			mid &= 0x3F;
			*(fresult++) = base64chars[mid];
			mid = tmp;
			mid >>= 12;
			mid &= 0x3F;
			*(fresult++) = base64chars[mid];
		}
		if (rest >= 2)
		{
			mid = tmp;
			mid >>= 6;
			mid &= 0x3F;
			*(fresult++) = base64chars[mid];
		}
		if (rest >= 3)
		{
			mid = tmp;
			mid &= 0x3F;
			*(fresult++) = base64chars[mid];
		}
		for (int c = 3; c > rest; c--)
		{
			*(fresult++) = '=';
		}
	}
	return finalresult;
}

char* CBase64Utils::Decode(char *input, int *bufsize)
{
	int std = 0, count = 1, resultlen = 0;
	char *finalresult = (char*)calloc(*bufsize + sizeof(char), sizeof(char));
	char *s = input, *result = finalresult;
	while (*s != '=' && count <= *bufsize)
	{
		//check to see if it's a legal base64 char...
		while (base64map[*s] == SKIP)
		{
			if (*s != '\r' && *s != '\n')
			{
				//bad char...
				//we might want to tell the user that there was error in the encoded data...
				ErrorCode = 1;
			}
			s++;
			(*bufsize)--;
			if (count >= *bufsize)
			{
				break;
			}
		}
		//add the base64 char to std...
		std |= base64map[*(s++) & 0xFF];
		std <<= 6;
		if (count % 4 == 0) //we have 3 more real chars...
		{
			//put std in the next 3 chars in finalresult
			int tmp;
			std >>= 6;
			tmp = std;
			tmp >>= 16;
			tmp &= 0xFF;
			*(result++) = (tmp);
			tmp = std;
			tmp >>= 8;
			tmp &= 0xFF;
			*(result++) = (tmp);
			tmp = std;
			tmp &= 0xFF;
			*(result++) = (tmp);
			std = 0; //empty std
			resultlen += 3;
		}
		count++;
	}
	//find and decode the remaining chars, if any...
	count--;
	if (count % 4 != 0)
	{
		//we have some remaining chars, now decode them...
		for (int i = 0; i < 4 - (count % 4); i++)
		{
			std <<= 6;
			resultlen++;
		}
		int tmp;
		std >>= 6;
		tmp = std;
		tmp >>= 16;
		tmp &= 0xFF;
		*(result++) = (tmp);
		tmp = std;
		tmp >>= 8;
		tmp &= 0xFF;
		*(result++) = (tmp);
		tmp = std;
		tmp &= 0xFF;
		*(result++) = (tmp);
	}
	*bufsize = resultlen;
	return finalresult;
}

CQPUtils::CQPUtils()
{
	ErrorCode = 0;
}

CQPUtils::~CQPUtils()
{
}

char* CQPUtils::Decode(char *input)
{
	int i;
	char *s = input;
	char *finalresult = (char*)calloc(strlen(input) + sizeof(char), sizeof(char));
	char *result = finalresult;
	while (*s != '\0') //loop through the entire string...
	{
		if (*s == '=') //woops, needs to be decoded...
		{
			for (int i = 0; i < 3; i++) //is s more than 3 chars long...
			{
				if (s[i] == '\0')
				{
					//error in the decoding...
					ErrorCode = 1;
					return finalresult;
				}
			}
			char mid[3];
			s++; //move past the "="
			//let's put the hex part into mid...
			bool ok = true;
			for (i = 0; i < 2; i++)
			{
				if (hexmap[s[i]] == SKIP)
				{
					//we have an error, or a linebreak, in the encoding...
					ok = false;
					if (s[i] == '\r' && s[i + 1] == '\n')
					{
						s += 2;
						//*(result++) = '\r';
						//*(result++) = '\n';
						break;
					}
					else
					{
						//we have an error in the encoding...
						ErrorCode = 1;
						//s--;
					}
				}
				mid[i] = s[i];
			}
			//now we just have to convert the hex string to an char...
			if (ok)
			{
				s += 2;
				int m = hexmap[mid[0]];
				m <<= 4;
				m |= hexmap[mid[1]];
				*(result++) = m;
			}
		}
		else
		{
			if (*s != '\0') *(result++) = *(s++);
		}
	}

	return finalresult;
}

#define BufAdd 10 //the size that we expands the buffer with...

char* CQPUtils::ExpandBuffer(char *buffer, int UsedSize, int *BufSize, bool SingleChar)
{
	//should we expand the buffer now...
	int AddVal;
	if (SingleChar) AddVal = 3;
	else AddVal = 5;
	if (UsedSize >= *BufSize - AddVal)
	{
		//expand the buffer
		*BufSize += BufAdd;
		return (char*)realloc(buffer, *BufSize * sizeof(char));
	}
	return buffer;
}

char* CQPUtils::Encode(char *input)
{
	int BufSize = strlen(input) + BufAdd; //size of the result buffer
	int UsedSize = 0; //used space in result buffer
	int LineLen = 0; //length of the current line...
	char *finalresult = (char*)calloc(BufSize, sizeof(char)); //the result buffer
	char *fresult = finalresult;
	char *s = input;
	while (*s != '\0')
	{
		//convert the signed char to an unsigned char...
		unsigned char mid = (256 - (0 - *s));
		//should we reset the linelength...
		if (*s == '\n')
			LineLen = 0; //we are starting on a new line...
		if (QpEncodeMap[mid] == SKIP)
		{
			//we need to encode this char...
			//is the line too long...
			if (LineLen >= MaxLineLength - 4)
			{
				//wrap the line...
				finalresult = ExpandBuffer(finalresult, UsedSize, &BufSize, false);
				*(fresult++) = '=';
				*(fresult++) = '\r';
				*(fresult++) = '\n';
				UsedSize += 3;
				LineLen = 0;
			}
			//check buffersize...
			finalresult = ExpandBuffer(finalresult, UsedSize, &BufSize, false);
			//add the hex value for the char...
			char mids[3];
			_itoa_s(mid, mids, 16);
			_strupr_s(mids,strlen(mids));
			*(fresult++) = '=';
			*(fresult++) = mids[0];
			*(fresult++) = mids[1];
			UsedSize += 3;
			LineLen += 2;
			s++;
		}
		else
		{
			//just add the char...
			//is the line too long...
			if (LineLen >= MaxLineLength - 4)
			{
				//wrap the line...
				finalresult = ExpandBuffer(finalresult, UsedSize, &BufSize, false);
				*(fresult++) = '=';
				*(fresult++) = '\r';
				*(fresult++) = '\n';
				UsedSize += 3;
				LineLen = 0;
			}
			//check buffersize...
			finalresult = ExpandBuffer(finalresult, UsedSize, &BufSize);
			UsedSize++;
			LineLen++;
			*(fresult++) = *(s++);
		}
	}
	*(fresult++) = '\0';
	return finalresult;
}